﻿/**
  NELMATE   ELMATEC   LM      EL  TECONELMA  CO         NE          MA   ONELMAT    ELMATECO  LMATECONE 
 ON        NE     CO  LMA     EL  TE         CO         NEL        LMA  CO      EC     AT     LM       
CO         NE     CO  LMAT    EL  TE         CO         NELM      ELMA  CO      EC     AT     LM
CO         NE     CO  LM TE   EL  TECONELM   CO         NE MA    NE MA  CONELMATEC     AT     LMATECONE
CO         KC     CO  LM  EC  EL  TE         CO         NE  AT  ON  MA  CO      EC     AT     LM
 ON        KC     CO  LM   CO EL  TE         CO         NE   TECO   MA  CO      EC     AT     LM
  NELMATE   ELMATEC   LM    ONEL  TECONELMA  CONELMATE  NE    EC    MA  CO      EC     AT     LMATECONE
_______________________________________________________________________________________________________
 ConelMate Framework 3.0 - copyright 2007(c) Ruben Sainz de la Maza
 
	@class: 	Connect
	@vertion: 	1.0
	@date:		25/02/08
				
	@language: 	ActionScript 3.0
	@author:	Ruben Sainz de la Maza
			
**/


package com.conelmate.nc
{
	
	import com.conelmate.util.Trace;
	import com.conelmate.nc.NetConn;
	import com.conelmate.nc.ProxyClient;
	
	import flash.net.NetConnection;
	import flash.events.NetStatusEvent;
    import flash.events.SecurityErrorEvent;	
	import flash.events.EventDispatcher;
	import flash.events.Event;
	import flash.net.ObjectEncoding;
	
	import flash.utils.Timer;
    import flash.events.TimerEvent;


	public class NetConnManager extends Trace
	{
		static	public	const 	CONNECT_SUCCESS		:String = "CONNECT_SUCCESS";		
		static	public	const 	CONNECT_CLOSED		:String = "CONNECT_CLOSED";
		static	public	const 	CONNECT_FAILED		:String = "CONNECT_FAILED";
		static	public	const 	CONNECT_REJECTED	:String = "CONNECT_REJECTED";		
		
		static internal var		_INSTANCE			:NetConnManager;				//instancia de esta clase (singleton)		
				private var 	_NC					:NetConnection;					//coneccion
				private var 	_objProxyClient		:ProxyClient;
				private	var 	_arrNCs				:Array;							//array de conecciones
				private var 	_timer				:Timer;							//usado para disparar diferentes conecciones cada 1 segundo
				private var 	_intConnection		:uint = 0;						//para llevar el indice dentro de _arrNCs
				private var 	_PORTS				:Array = [80,443,1935];
				private var	 	_RTMP				:String = "127.0.0.1";
				private var	 	_RTMP_INSTANCE		:String = "CAK";
				private var 	_ROOM				:String = "lobby";
				private var 	_ARGUMENTS;
				private var		_strError			:String = "";
				private var		_strMsg				:String = "";				
								
				

//_________________________________________________________________________________ Constructor	
		public function NetConnManager(enforcer:SingletonEnforcer)
		{
			_isTrace = false;
			mTrace("#NetConnManager# constructor");
		}
		


//_________________________________________________________________________________ Public Methods

		/**
		 * singleton
		 * devolver una instancia de la clase
		 */	
		public static function getInstance():NetConnManager
		{
			if(NetConnManager._INSTANCE == null) NetConnManager._INSTANCE = new NetConnManager(new SingletonEnforcer());
			return NetConnManager._INSTANCE;
		}
		
		
		/**
		 * inicializar
		 */		
		public function init():void
		{
			mTrace("#NetConnManager# init");
			
			_arrNCs = new Array();
			
			for(var i:uint = 0; i < _PORTS.length; i++){

				var objNetConn:NetConn = new NetConn(_RTMP, _PORTS[i], _RTMP_INSTANCE, _ROOM);
				
				objNetConn.addEventListener(NetConn.CONNECT_SUCCESS, 	onConnect);			
				objNetConn.addEventListener(NetConn.CONNECT_FAILED, 	onFailed);
				objNetConn.addEventListener(NetConn.CONNECT_REJECTED, 	onRejected);
				objNetConn.addEventListener(NetConn.SECURITY_ERROR, 	onSecurityError);
				objNetConn.arguments = _ARGUMENTS;
				_arrNCs[i] = { netConn:objNetConn, failed:false };
			}
			
			_timer = new Timer(1000, _arrNCs.length);
            _timer.addEventListener(flash.events.TimerEvent.TIMER, nextConnect);
            _timer.start();
		}
		
		
		/**
		 * destruir objetos y variables
		 */	
		public function destroy():void
		{
			mTrace("#NetConnManager# destroy");

			if(_NC){
				_NC.close();
				_NC = null;
			}
			
			NetConnManager._INSTANCE = null;
		}		
		
		

		
//_________________________________________________________________________________ Private Methods	

		/**
		 * intentar la siguiente coneccion
		 */		
		private function nextConnect(e:TimerEvent):void
		{
			var isTryConnect = _arrNCs[_intConnection].netConn.connect();
			mTrace("#NetConnManager# nextConnect: " + isTryConnect);
			_intConnection++;
		}


		/**
		 * detener todas las conecciones, menos la que se ha logrado
		 */	
		private function stopConnections(netConn:NetConn = null):void
		{
			mTrace("#NetConnManager# stopConnections");
			
			_timer.stop();
			if(netConn != null) 
				for(var i:uint = 0; i < _arrNCs.length; i++) 
					if(_arrNCs[i].netConn != netConn) 
						if(_arrNCs[i].netConn.nc) 
							_arrNCs[i].netConn.destroy();
		}


		/**
		 * al lograr una coneccion valida
		 */		
		private function onConnect(e:Event):void
		{
			var objNetConn:NetConn = e.target;
			
			stopConnections(objNetConn); //detener el resto de las conecciones
			mTrace("#NetConnManager# conectado:" + objNetConn.strConnection);
			
			objNetConn.addEventListener(NetConn.CONNECT_CLOSED, 		onClosed);
			objNetConn.addEventListener(NetConn.CONNECT_APPSHUTDOWN, 	onAppShutDown);
			objNetConn.addEventListener(NetConn.CONNECT_INVALIDAPP,		onInvalidApp);			
			objNetConn.addEventListener(NetConn.CALL_FAILED, 			onCallFailed);
			objNetConn.addEventListener(NetConn.CALL_PROHIBITED, 		onCallProhibited);
			
			objNetConn.removeEventListener(NetConn.CONNECT_SUCCESS, 	onConnect);			
			objNetConn.removeEventListener(NetConn.CONNECT_FAILED, 		onFailed);
		//	objNetConn.removeEventListener(NetConn.CONNECT_REJECTED, 	onRejected);
			objNetConn.removeEventListener(NetConn.SECURITY_ERROR, 		onSecurityError);

			_NC = objNetConn.nc;
			
			_objProxyClient = ProxyClient.getInstance();
			_objProxyClient.nc = _NC;
			_NC.client = _objProxyClient;
			
			dispatchEvent(new Event(NetConnManager.CONNECT_SUCCESS));
		}
		
		
		/**
		 * al cerrarse la coneccion
		 */		
		private function onClosed(e:Event):void
		{
			mTrace("#NetConnManager# onClosed");
			dispatchEvent(new Event(NetConnManager.CONNECT_CLOSED));
		}


		/**
		 * al fallar la coneccion
		 * verificar si todas han fallado, y despachar el evento CONNECT_FAILED
		 */		
		private function onFailed(e:Event):void
		{
			mTrace("#NetConnManager# onFailed target:" + e.target.strConnection);
			
			var failed:uint = 0;
			var i:uint;
			var l:uint = _arrNCs.length;
			
			for(i = 0; i < l; i++){
				if(_arrNCs[i].netConn == e.target) _arrNCs[i].failed = true;
				if(_arrNCs[i].failed) failed++;
			}

			if(failed >= _arrNCs.length) dispatchEvent(new Event(NetConnManager.CONNECT_FAILED));
		}


		/**
		 * al ser rechazada la coneccion
		 */		
		private function onRejected(e:Event):void
		{
			mTrace("#NetConnManager# onRejected " + e);
			_strError = e.target.error;
			_strMsg	  = e.target.msg;

			stopConnections();
			dispatchEvent(new Event(NetConnManager.CONNECT_REJECTED));
		}
		
		
		/**
		 * al finalizar la aplicacion del lado del servidor
		 */		
		private function onAppShutDown(e:Event):void
		{
			mTrace("#NetConnManager# onAppShutDown " + e);
		}
		
		
		/**
		 * al no encontrarse una aplicacion valida
		 */		
		private function onInvalidApp(e:Event):void
		{
			mTrace("#NetConnManager# onInvalidApp " + e);
		}
		
		
		/**
		 * al fallar una llamada a una metodo en el servidor
		 */		
		private function onCallFailed(e:Event):void
		{
			mTrace("#NetConnManager# onCallFailed " + e);
		}
		
		
		/**
		 * al llamar a un metodo al que no se puede acceder
		 */		
		private function onCallProhibited(e:Event):void
		{
			mTrace("#NetConnManager# onCallProhibited " + e);
		}		


		/**
		 * al producirse un error de seguridad
		 */		
		private function onSecurityError(e:Event):void
		{
			mTrace("#NetConnManager# securityErrorHandler " + e);
		}


//_________________________________________________________________________________ GETTERS

		public function get nc		():NetConnection{ return _NC; }
		public function get rtmp	():String 		{ return _RTMP; }
		public function get ports	():Array 		{ return _PORTS; }
		public function get instance():String 		{ return _INSTANCE; }
		public function get room	():String 		{ return _ROOM; }
		public function get error	():String		{ return _strError; }
		public function get msg		():String		{ return _strMsg; }		

//_________________________________________________________________________________ SETTERS

		public function set rtmp		(p:String)	{ _RTMP = p; }
		public function set ports		(p:Array)	{ _PORTS = p; }
		public function set instance	(p:String)	{ _INSTANCE = p; }
		public function set room		(p:String)	{ _ROOM = p; }
		public function set arguments	(p)			{ _ARGUMENTS = p; }
		
	}
}

internal class SingletonEnforcer{}
